package com.keyware.kd.service.impl.shiro;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import com.keyware.utils.ProjectIdentNew;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.keyware.kd.exception.ExpirationTimeException;
import com.keyware.kd.exception.LoginForbiddenException;
import com.keyware.kd.exception.MaxSessionException;
import com.keyware.kd.exception.NoRelatedRolesException;
import com.keyware.kd.exception.PasscodeErrorException;
import com.keyware.kd.exception.SecuritySetException;
import com.keyware.kd.exception.SingleIpLoginException;
import com.keyware.kd.listener.SessionCounter;
import com.keyware.kd.repository.entity.Permission;
import com.keyware.kd.repository.entity.Role;
import com.keyware.kd.repository.entity.number.NumberBuilder;
import com.keyware.kd.repository.entity.safemanagement.SafeManagement;
import com.keyware.kd.repository.entity.user.User;
import com.keyware.kd.service.itf.auditlog.AuditLogService;
import com.keyware.kd.service.itf.number.NumberBuilderService;
import com.keyware.kd.service.itf.safemanagement.SafeManagementService;
import com.keyware.kd.service.itf.shiro.IShiroAuth;
import com.keyware.kd.service.itf.user.UserService;
import com.keyware.kd.utils.Constant;
import com.keyware.utils.DateUtils;
import com.keyware.utils.EncryptUtil;


@Component("MyRealm")
public class MyRealm extends SimpleAccountRealm {

    @Autowired
    protected IShiroAuth shiroAuth;

    @Resource
    private UserService userService;

    @Resource
    private SafeManagementService safeManagementService;

    @Resource
    private NumberBuilderService numberBuilderService;

    @Autowired
    HttpServletRequest request;

    @Autowired
    private SessionDAO sessionDAO;

    public MyRealm() {
        setName("MyRealm");
		/*PasswordService ps = new DefaultPasswordService();
		PasswordMatcherEx pme = new PasswordMatcherEx();
		pme.setPasswordService(ps);
		setCredentialsMatcher(pme);*/
    }

    @PostConstruct
    public void initCredentialsMatcher() {
        //该句作用是重写shiro的密码验证，让shiro用我自己的验证
        setCredentialsMatcher(new CheckPassword());
    }


    @Override
    /**
     * 权限验证
     */
    public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        UsernamePasswordToken upt = (UsernamePasswordToken) token;
        String username = upt.getUsername();
        char[] pwd = upt.getPassword();
        String password = String.valueOf(pwd);
        String numCode = numberBuilderService.getCustomerId();
        String loginIp = request.getRemoteAddr();
        String idcard = (String) request.getAttribute("idcard");
        int num = 0;
        String loginIp3 = "";
        Collection<Session> sessions = sessionDAO.getActiveSessions();//获取当前已登录的用户session列表
        for (Session session : sessions) {
            //清除该用户以前登录时保存的session
            if (username.equals(String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY)))) {
                String loginIp2 = SessionCounter.sessionIpMap.get(session.getId());
                if (null != loginIp2 && !loginIp2.equals(loginIp)) {
                    loginIp3 = loginIp2;
                    num = 1;
                } else {
                    num = 2;
                }
            }
        }

        NumberBuilder isLoginIpNumber = numberBuilderService.selectByPrimaryKey("isLoginIp");
        if (null != isLoginIpNumber && "1".equals(isLoginIpNumber.getNumber())) {
            if (num == 1) {
                throw new SingleIpLoginException("该用户已在" + loginIp3 + "登录");
            } else if (num == 2) {
                throw new SingleIpLoginException("该用户已登录");
            }
        }


        Integer sessionCount = Constant.sessionCount;
        if (0 != sessionCount) {
            if (SessionCounter.getActiveSessions() >= sessionCount) {
                throw new MaxSessionException("平台同时在线人数已达到上限");
            }
        }

        if (username == null) {
            throw new AccountException("用户名不能为空!");
        }

        User user = shiroAuth.findSubject(username);// 带角色权限菜单的
        if (user == null) {
            throw new UnknownAccountException("用户名不存在!");
        }

        //1.验证用户状态
        User userInfo = userService.selectUserByUserId(username);
        //获取当前设置的平台密级
        NumberBuilder numberBuilder = numberBuilderService.selectByPrimaryKey("platformPwdLevel");
        List<SafeManagement> safeManagements = null;
        SafeManagement safeManagement = null;
        if (null == numberBuilder || numberBuilder.getNumber().equals("platform")) {//平台密级
            safeManagements = safeManagementService.selectInfo();//获取当前平台最小密级
            if (safeManagements.size() != 1) {
                throw new SecuritySetException("安全设置重复,请联系开发商!");
            }
            safeManagement = safeManagements.get(0);
        } else {//用户密级
            List<SafeManagement> safeManagementList = safeManagementService.selectAll();
            String userRankId = userInfo.getUesrRankId() == null ? "" : userInfo.getUesrRankId();
            if (!userRankId.equals("") && safeManagementList.size() > 0) {
                for (SafeManagement safeManagement2 : safeManagementList) {
                    if (userRankId.equals(safeManagement2.getId())) {
                        safeManagement = safeManagement2;
                    }
                }
            }
            if (null == safeManagement) {
                safeManagements = safeManagementService.selectInfo();//获取当前平台最小密级
                if (safeManagements.size() != 1) {
                    throw new SecuritySetException("安全设置重复,请联系开发商!");
                }
                safeManagement = safeManagements.get(0);
            }
        }

        if (!username.equals("admin") && userInfo != null && null != userInfo.getUserStatus() && 1 == userInfo.getUserStatus()) {
            throw new DisabledAccountException("用户 [" + username + "] 未启用.");
        }
        if (numCode != null && numCode.equals(ProjectIdentNew.tianjin712)) {
            String nowTime = DateUtils.getDateTime();
            String pwdErrorDate = userInfo.getUserPwdErrorDate();
            if (pwdErrorDate != null && userInfo.getUserPwdError() == safeManagement.getPwdLocktimes()) {
                long time = getDatePoor(nowTime, pwdErrorDate);
                if (1 == userInfo.getUserLock() && time >= 30) {
                    List<String> userIds = new ArrayList<String>();
                    userInfo.setUserLock((long) 0);
                    userIds.add(userInfo.getId());
                    userService.unlock(userIds);
                }
            }
        }
        //2.验证用户是否被锁定
        if (!username.equals("admin") && userInfo != null && null != userInfo.getUserLock() && (1 == userInfo.getUserLock() || "1".equals(userInfo.getUserLock()))) {
            throw new LockedAccountException("用户 [" + username + "] 已被锁定.");
        }

        if (1 == userInfo.getLogout()) {
            throw new LoginForbiddenException("该用户已注销！");
        }
        if (!"admin".equals(user.getUserId()) && !"safesecret".equals(user.getUserId()) && !"safeaudit".equals(user.getUserId()) && !"sysadmin".equals(user.getUserId())) {
            //验证密码是否失效
            BigDecimal pwdExpirationTime = safeManagement.getPwdExpirationTime();//以天为单位
            long expirationMin = pwdExpirationTime.multiply(new BigDecimal(24)).multiply(new BigDecimal(60)).longValue();//失效时长(分钟)

            String userPwdModifTime = user.getUserPwdModifTime();

            //确保之前数据可以转换成功
            if (null != userPwdModifTime && userPwdModifTime.length() < 15) {
                userPwdModifTime += " 00:00";
            }
            if (null != userPwdModifTime && userPwdModifTime.length() == 19) {
                userPwdModifTime = userPwdModifTime.substring(0, 15);
            }
            long betweenDays = DateUtils.calculateTimeDifferenceByCalendar(userPwdModifTime, DateUtils.getDate("yyyy-MM-dd HH:mm"));
            if (betweenDays > expirationMin) {
                throw new PasscodeErrorException("密码已失效");
            }
        }

        if ("sysadmin".equals(username) && (!EncryptUtil.md5(password).equals(userInfo.getPassword()))) {
            throw new UnknownAccountException("该用户不存在!");
        }

        //3.判断密码输入是否错误，错误后是否锁定用户
        if (!"sysadmin".equals(username) && !username.equals("admin") && userInfo != null && (!EncryptUtil.md5(password).equals(userInfo.getPassword()) && userInfo.getPassword() != EncryptUtil.md5(password)) && StringUtils.isEmpty(idcard)) {
            Long errorTime = userInfo.getUserPwdError();
            String errorDate = userInfo.getUserPwdErrorDate();
            if (errorDate != null) {
                //3.1当天有连续错误记录
                if (errorDate.equals(DateUtils.getDate())) {
                    errorTime += 1;
                    long pwd_lockTimes = 0;
                    if (safeManagement != null && safeManagement.getPwdLocktimes() != null && !"".equals(safeManagement.getPwdLocktimes())) {
                        pwd_lockTimes = safeManagement.getPwdLocktimes();
                    }
                    if (pwd_lockTimes != 0) {
                        if (errorTime >= pwd_lockTimes) {
                            userInfo.setUserPwdError(errorTime);
                            userInfo.setUserLock((long) 1);
                            userInfo.setUserPwdErrorDate(DateUtils.getDateTime());
                            userService.updateByPrimaryKey(userInfo);
                            throw new LockedAccountException("密码错误次数达到限定值" + pwd_lockTimes + ",用户 [" + username + "] 已经被锁定.");
                        } else {
                            userInfo.setUserPwdError(errorTime);
                            userInfo.setUserPwdErrorDate(DateUtils.getDate());
                            userService.updateByPrimaryKey(userInfo);
                            throw new IncorrectCredentialsException("用户 [" + username + "]密码不正确.");
                        }
                    }
                } else {
                    //3.2当天第一次错误记录
                    errorTime = (long) 1;
                    errorDate = DateUtils.getDate();
                    userInfo.setUserPwdError(errorTime);
                    userInfo.setUserPwdErrorDate(errorDate);
                    userService.updateByPrimaryKey(userInfo);
                }
            } else {
                //3.3当天第一次错误记录
                errorTime = (long) 1;
                errorDate = DateUtils.getDate();
                userInfo.setUserPwdError(errorTime);
                userInfo.setUserPwdErrorDate(errorDate);
                userService.updateByPrimaryKey(userInfo);
            }
        }

        //4.如果密码正确,清空错误记录
        if (userInfo != null && (EncryptUtil.md5(password).equals(userInfo.getPassword()) || userInfo.getPassword() == EncryptUtil.md5(password)) && StringUtils.isEmpty(idcard)) {
            userInfo.setUserPwdError((long) 0);
            userInfo.setUserPwdErrorDate(null);
            userService.updateByPrimaryKey(userInfo);
        }

        //三员账号不做活跃时间判断
        if (!"admin".equals(user.getUserId()) && !"safesecret".equals(user.getUserId()) && !"safeaudit".equals(user.getUserId()) && !"sysadmin".equals(user.getUserId())) {
            //5.验证用户是否超出非活跃时间
            if (!username.equals("admin") && userInfo != null && safeManagement != null && safeManagement.getUserInactiveTime() != null && !"".equals(safeManagement.getUserInactiveTime())) {
                //5.1、获取非活跃时间限定值(天)
                BigDecimal userInactiveTime = safeManagement.getUserInactiveTime();
                //5.2获取上一次退出的时间
                String exitTime = user.getExitTime();
                //5.3本次登录的时间-上一次退出时间=非活动时间范围是否大于限定值
                //5.4判断是否锁定该用户
                if (userInactiveTime != null && userInactiveTime.intValue() != 0) {
                    long userInactiveTimeMin = userInactiveTime.multiply(new BigDecimal(24)).multiply(new BigDecimal(60)).longValue();//非活跃时间限定值(分钟)
                    //确保之前数据可以转换成功
                    if (null != exitTime && exitTime.length() < 15) {
                        exitTime += " 00:00";
                    }
                    if (null != exitTime && exitTime.length() == 19) {
                        exitTime = exitTime.substring(0, 15);
                    }
                    long between_days = DateUtils.calculateTimeDifferenceByCalendar(exitTime, DateUtils.getDate("yyyy-MM-dd HH:mm"));

                    //5.6判断非活动时间范围是否大于限定值
                    if (between_days > userInactiveTimeMin) {
                        //5.7更新退出时间为当前时间，并锁定用户
                        userInfo.setUserLock((long) 1);
                        userService.updateByPrimaryKey(userInfo);
                        throw new ExpirationTimeException("用户超出非活跃规定时间,锁定用户.");
                    }
                }
                //5.7更新退出时间
                userInfo.setExitTime(DateUtils.getDate("yyyy-MM-dd HH:mm"));
                userService.updateByPrimaryKey(userInfo);
            }
        }

        //6.验证是否有登陆权限
        if (CollectionUtils.isEmpty(user.getRoles()) || !StringUtils.hasText(user.getRoles().get(0).getRoleId())) {
            throw new NoRelatedRolesException("用户 [" + username + "] 无权限");
        }
        CheckPassword.idcard = idcard;
        SimpleAuthenticationInfo saInfo = new SimpleAuthenticationInfo(username, user.getPassword(), getName());

        return saInfo;
    }

    /**
     * 授权
     */
    @Override
    public AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        User user = shiroAuth.findSubject(getUsername(principals));
        if (user instanceof User) {
            SimpleAuthorizationInfo sarpinfo = new SimpleAuthorizationInfo();
            //角色
            for (Role role : user.getRoles()) {
                sarpinfo.addRole(role.getRoleCode());
            }

            //权限
            if (!CollectionUtils.isEmpty(user.getPermissions())) {
                for (Permission permission : user.getPermissions()) {
                    if (StringUtils.hasText(permission.getPermission())) {
                        sarpinfo.addStringPermission(permission.getPermission());
                    }
                }
            }
            return sarpinfo;
        } else {
            return null;
        }
    }

    //计算相隔时间查
    public long getDatePoor(String nowTime, String pwdErrorDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date endDate = null;
        Date nowDate = null;
        try {
            endDate = formatter.parse(nowTime);
            nowDate = formatter.parse(pwdErrorDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        long between = (endDate.getTime() - nowDate.getTime()) / 1000;
        long min = between / 60;
        return min;
    }
}